#ifndef _EXAMPLEFUNCTIONS_H_
#define _EXAMPLEFUNCTIONS_H_

#include "VectorFunction.H"

#include "NamespaceHeader.H"
/**
 * @brief 1.This example represents exact solutions for 2D-INSE.
 */
class TestExactSol2D : public VectorFunction
{
protected:
    Real mu = 0.001;

public:
    TestExactSol2D(){};
    ~TestExactSol2D(){};
    const Vector<Real> operator()(const RealVect &x, const Real &time = 0) const;
    /// The returned vector is a combination of (u,v) and p.
    const int getNumComponents(void) const { return 3; };
    const Real getmu(void) const { return mu; };
    void setmu(Real _mu) { mu = _mu; };
};

const Vector<Real> TestExactSol2D::operator()(const RealVect &x, const Real &time) const
{
    Real u = x[0];
    Real v = x[1];
    Vector<Real> value;
    Real temp = exp(-2.0 * M_PI * M_PI * mu * time);
    Real val_1 = temp * (-cos(M_PI * u) * sin(M_PI * v));
    Real val_2 = temp * (sin(M_PI * u) * cos(M_PI * v));
    Real val_3 = -0.25 * exp(-4.0 * M_PI * M_PI * mu * time) * (cos(2 * M_PI * u) + cos(2 * M_PI * v));
    value.push_back(val_1);
    value.push_back(val_2);
    value.push_back(val_3);
    return value;
}

/**
 * @brief 2.This example represents exact solutions for 3D-INSE.
 */
class TestExactSol3D : public VectorFunction
{
protected:
    Real mu = 0.001;

public:
    TestExactSol3D(){};
    ~TestExactSol3D(){};
    const Vector<Real> operator()(const RealVect &x, const Real &time = 0) const;
    /// The returned vector is a combination of (u,v,w) and p.
    const int getNumComponents(void) const { return 4; };
    const Real getmu(void) const { return mu; };
    void setmu(Real _mu) { mu = _mu; };
};

const Vector<Real> TestExactSol3D::operator()(const RealVect &x, const Real &time) const
{
    Real u = x[0];
    Real v = x[1];
    Real w = x[2];
    Vector<Real> value;
    Real temp = exp(-3.0 * M_PI * M_PI * mu * time);
    Real val_1 = temp * 2.0 * (-cos(M_PI * u) * sin(M_PI * v) * sin(M_PI * w));
    Real val_2 = temp * (sin(M_PI * u) * cos(M_PI * v) * sin(M_PI * w));
    Real val_3 = temp * (sin(M_PI * u) * sin(M_PI * v) * cos(M_PI * w));
    Real val_4 = -0.25 * exp(-6.0 * M_PI * M_PI * mu * time) * (cos(2 * M_PI * u) + cos(2 * M_PI * v) + cos(2 * M_PI * w));
    value.push_back(val_1);
    value.push_back(val_2);
    value.push_back(val_3);
    value.push_back(val_4);
    return value;
}

/**
 * @brief 3.This example represents a trivial 2D vector function which scales up X by 100.
 */
class TestTrivial2D : public VectorFunction
{
public:
    TestTrivial2D(){};
    ~TestTrivial2D(){};
    const Vector<Real> operator()(const RealVect &x, const Real &time = 0) const
    {
        Vector<Real> value;
        value.push_back(100 * x[0]);
        value.push_back(100 * x[1]);
        value.push_back(100 * (x[0] + x[1]));
        return value;
    };
    /// The returned vector is a combination of (u,v) and p.
    const int getNumComponents(void) const { return 3; };
};

/**
 * @brief 4.This example represents a trivial 3D vector function which scales up X by 100.
 */
class TestTrivial3D : public VectorFunction
{
public:
    TestTrivial3D(){};
    ~TestTrivial3D(){};
    const Vector<Real> operator()(const RealVect &x, const Real &time = 0) const
    {
        Vector<Real> value;
        value.push_back(100 * x[0]);
        value.push_back(100 * x[1]);
        value.push_back(100 * x[2]);
        value.push_back(100 * (x[0] + x[1] + x[2]));
        return value;
    };
    /// The returned vector is a combination of (u,v,w) and p.
    const int getNumComponents(void) const { return 4; };
};

#include "NamespaceFooter.H"

#else
// do nothing
#endif